package com.dabai.iot.mqtt.event;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.dabai.common.core.enums.YnEnum;
import com.dabai.common.core.utils.DateUtils;
import com.dabai.iot.common.enums.BizLogCode;
import com.dabai.iot.common.enums.ConnectStatus;
import com.dabai.iot.common.model.DeviceInfo;
import com.dabai.iot.common.model.IotMqttStatus;
import com.dabai.iot.core.api.RemoteDeviceService;
import com.dabai.iot.core.api.RemoteMessageHandlerService;
import com.dabai.iot.core.entity.DeviceEntity;
import com.dabai.iot.mqtt.model.DeviceConnectionSession;
import com.dabai.iot.mqtt.util.ClientUtil;
import com.dabai.iot.mqtt.util.MqttSessionManager;
import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
import io.vertx.mqtt.MqttAuth;
import io.vertx.mqtt.MqttEndpoint;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;

/**
 * 设备连接状态监听
 *
 * @author kev1n
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DeviceConnectStatusListener implements IMqttConnectStatusListener {

    @DubboReference(stub = "true")
    private final RemoteDeviceService remoteDeviceService;

    @DubboReference
    private final RemoteMessageHandlerService remoteMessageHandlerService;

    /**
     * 设备上线
     */
    @Override
    public boolean online(MqttEndpoint endpoint) {
        final String clientId = endpoint.clientIdentifier();
        MqttAuth mqttAuth = endpoint.auth();

        // 对于设备连接，username 即为产品Key，初始 endpoint.clientIdentifier() 即为设备序列号
        String productKey = mqttAuth.getUsername();
        String deviceSn = endpoint.clientIdentifier();

        DeviceEntity device = remoteDeviceService.getDeviceCacheBySn(productKey, deviceSn);
        if (device == null) {
            log.info("MQTT Server ====> 设备连接失败，设备不存在，clientId={}", clientId);
            endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED);
            return false;
        }

        if (!device.getDeviceSecret().equals(mqttAuth.getPassword())) {
            log.info("MQTT Server ====> 设备连接失败，用户名或密码错误，clientId={}", clientId);
            endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD);
            return false;
        }

        if (YnEnum.NO.getValue().equals(device.getIsEnabled())) {
            log.info("MQTT Server ====> 设备连接失败，此设备已禁用，clientId={}", clientId);
            endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED);
            return false;
        }

        // 把产品Key+设备序列号做为新的 clientIdentifier
        endpoint.setClientIdentifier(ClientUtil.deviceClientId(device.getProductKey(), device.getDeviceSn()));
        // accept connection from the remote client
        return acceptDevice(endpoint, device);
    }

    /**
     * 设备离线
     */
    @Override
    public void offline(MqttEndpoint endpoint) {
        final String clientId = endpoint.clientIdentifier();
        DeviceConnectionSession session = MqttSessionManager.getInstance().getSession(clientId);
        if (session == null) {
            return;
        }

        // 移除连接会话，返回 true 代表设备正常下线，记录下线日志
        boolean result = MqttSessionManager.getInstance().removeSession(clientId, endpoint);
        if (result) {
            // 发送设备下线消息
            publishDeviceStatus(session.getDeviceInfo(), ConnectStatus.offline);
        }
    }

    /**
     * 接受有效客户端连接
     */
    private boolean acceptDevice(MqttEndpoint endpoint, DeviceEntity device) {
        endpoint.accept(false);

        DeviceInfo deviceInfo = new DeviceInfo(
            device.getTenantId(),
            device.getProductId(),
            device.getProductKey(),
            device.getId(),
            device.getDeviceSn(),
            device.getDataType()
        );

        // 保存设备连接会话
        DeviceConnectionSession connectionSession = new DeviceConnectionSession(deviceInfo, endpoint);
        ConnectStatus connectStatus = MqttSessionManager.getInstance().addSession(endpoint.clientIdentifier(), connectionSession);

        // 发送设备状态消息
        publishDeviceStatus(deviceInfo, connectStatus);

        log.info("MQTT Server ====> 当前设备连接数 [{}]", MqttSessionManager.getInstance().getSessionCount());

        return true;
    }

    /**
     * 发送设备状态消息
     */
    private void publishDeviceStatus(DeviceInfo deviceInfo, ConnectStatus connectStatus) {
        String traceId = IdWorker.get32UUID();
        String clientId = ClientUtil.deviceClientId(deviceInfo.productKey(), deviceInfo.deviceSn());
        log.info("MQTT Server ====> 将设备状态发往core处理: traceId={}  clientId={}", traceId, clientId);
        IotMqttStatus iotMqttStatus = new IotMqttStatus(traceId, connectStatus, BizLogCode.SUCCESS, DateUtils.getNowDate());
        remoteMessageHandlerService.deviceStatusHandler(deviceInfo, iotMqttStatus);
    }

}
